<?php
/**
* Library of commonly-used functions
* @package folio
**/
require_once ( 'model/user.php');
require_once $CFG->dirroot . 'mod/folio/config.php';
require_once $CFG->dirroot . 'mod/folio/sidebar.php';



/**
* Logging function for page hits
**/
function folio_history_pagelog( folio_page $page, $action, $tag = '' ) {
	global $FOLIO_CFG;
	
	// Provide option to not log.
	if( $FOLIO_CFG->track_pagelog ) {

		// Validity check
		if( ! ($action == 'view' ||
			$action == 'edit' ||
			$action == 'popup_listed' ||
			$action == 'popup_viewed' ||
			$action == 'pageofpopups_listed' ))
		{
			die('Invalid action "' . $action . '" passed to folio_history_pagelog');
		}

	    $log = new StdClass;
		
		if( isloggedin() )
			$log->user_ident = $_SESSION['userid'];
		else
			$log->user_ident = 0;
			
		if( $page == null ) {
			// Not looking at a specific page.
			$log->page_ident = -1;
			$log->owner_ident = -1;
		} else {
			$log->page_ident = intval( $page->page_ident);
			$log->owner_ident = intval($page->user_ident);
		}
		
		// Save the tag on the end of the action variable.
	    $log->action = $action . $tag;
	    $log->logged = time();

	    // Insert new record into db.
	    insert_record('folio_history_pagelog',$log);
	}
}



/**
* Listen for any api events
* Create the html code to overwrite the main page
* Record any log-on events
**/
function folio_init() {
    global $CFG;
    global $FOLIO_CFG;
    global $_SESSION;

    // Add events for folio
    listen_for_event("folio_page","updated","folio_rss_page_updated");
    listen_for_event("folio_comment","inserted","folio_rss_page_comment_inserted");
    
    listen_for_event("weblog_post","delete","folio_rss_weblog_delete");
    listen_for_event("weblog_post","publish","folio_rss_weblog_post");
    listen_for_event("weblog_post","update","folio_rss_weblog_post");
    listen_for_event("weblog_comment","publish","folio_rss_weblog_comment_post");
    listen_for_event("weblog_comment","delete","folio_rss_weblog_comment_delete");
    listen_for_event("file","publish","folio_rss_file_post");
    listen_for_event("file","update","folio_rss_file_post");
    listen_for_event("file","delete","folio_rss_file_delete");

    // Test to see if there has been a log-on event
    $l = optional_param('username');
    $p = optional_param('password');

    if ( $FOLIO_CFG->track_logons == 'Y' ) {
        // Test to see if a log-on event is going to occur thru cached credentials (or if they're already logged in, but
        //      the session variable with the replacement template code isn't being used).
        // Also test to see if a logon form has been submitted, in which case, re-test
        //    against authenticate & then record the logon event.
        if ( isloggedin() && !isset( $_SESSION['folio_template_frontpage'] ) ) {
            // Login via cached information.
            folio_record_logon();
        } else {
            // Test to see if a logon-form has been submitted.
            if (!empty($l) && !empty($p)) {
                if ( authenticate_account($l, $p) ) {
                    folio_record_logon();
                }
            }
        }
    }
}

/**
* Process stuff when a user first logs on
*
* Set $_SESSION variables
*   folio_template_frontpage: used later on to replace the frontpage_loggedin value to
*       create a custom start page. Values replaced by folio_pagesetup.php
*   folio_lastlogon: used later for the inbox sidemenu w activity since last logon.
**/
function folio_record_logon() {
    global $_SESSION;
    global $FOLIO_CFG;
    global $CFG;

    // Generate the front-page text & save the generated html in a session variable
    //   This variable is then used later on to replace the $template['frontpage_loggedin'] var.
    $_SESSION['folio_template_frontpage'] = 'true';

    // Get last log-on from folio_history_logon table
    $logon = recordset_to_array( get_recordset_sql( "SELECT concat(user_ident, '.', created ) c1, " .
        "user_ident, created FROM " . $CFG->prefix . "folio_history_logon ".
        "WHERE user_ident = {$_SESSION['userid']} ORDER BY created DESC LIMIT 1" ) );

    if ( is_array( $logon ) ) {
        // User has logged on before
        $logon =  array_pop($logon);
        $_SESSION['folio_lastlogon'] = $logon->created;
    } else {
        // Hasn't logged on before.
        $_SESSION['folio_lastlogon'] = time();
    }

    $ul = new StdClass;
    $ul->user_ident = $_SESSION['USER']->ident;
    $ul->created = time();

    // Test to see if we're keeping a historical record of logons
    if ($FOLIO_CFG->track_logons == 'Y') {
        // Insert new record into db.
        insert_record('folio_history_logon',$ul);
    } else {
        // Delete previous
        delete_records('folio_history_logon', 'user_ident', $ul->user_ident);
        insert_record('folio_history_logon',$ul);
    }


}

/**
* Create the menu links used by the folio add-in, as well as setting the commands for the info / navigation menu
* on the side of the page.
*/
function folio_pagesetup() {
    global $profile_id;
    global $page_owner;
    global $PAGE;
    global $CFG;
    global $FOLIO_CFG;
    global $template;
    global $_SESSION;
    global $metatags;
    global $function;
    // These are defined by view.php, and are needed to properly setup the edit & history links for the submenu.
    global $page_ident;
    global $page_title;
    global $username; // this is who we're looking at, not the logged in user.
            // Depreciated in v.7?  Start referring to profile_id

    $currentusername = $_SESSION['username'];
    $currentuserident = $_SESSION['userid'];

    // -----------------------------------------------
    // SETUP MENU
    //
    // Add Wiki Link
    if (isloggedin() & defined('context') ) {
        if ( substr(context, 0, 5) == "folio" & $page_owner == $currentuserident) {
                // Show selected
            $PAGE->menu[] = array( 'name' => 'folio',
                'html' => '<li><a href="'.$CFG->wwwroot . $currentusername . '/page/" class="selected">Your Pages</a></li>');
        } else {
            // Show main menu unselected
            $PAGE->menu[] = array( 'name' => 'folio',
                'html' => '<li><a href="'.$CFG->wwwroot . $currentusername . '/page/">Your Pages</a></li>');
        }
    } elseif ( isloggedin() ) {
        // Some pages don't have context defined.
        // Show link to folio.
        $PAGE->menu[] = array( 'name' => 'folio',
            'html' => '<li><a href="'.$CFG->wwwroot . $currentusername . '/page/">Your Pages</a></li>');
    }

    // Add Activity Link (as first option)
    if (isloggedin() & defined('context') ) {
        if ( context == "feeds" & $page_owner == $currentuserident) {
            // Show selected
            array_splice( $PAGE->menu, 0,0, array( array( 'name' => 'folio_feeds',
                'html' => '<li><a href="'.$CFG->wwwroot . 'activity/fac/' . $currentusername . '/summary/all/all/1" class="selected">Your Activity</a></li>')));
        } else {
            // Show main menu unselected
            array_splice( $PAGE->menu, 0,0, array( array( 'name' => 'folio_feeds',
                'html' => '<li><a href="'.$CFG->wwwroot . 'activity/fac/' . $currentusername . '/summary/all/all/1" >Your Activity</a></li>')));

        }
    } elseif ( isloggedin() ) {
        // Some pages don't have context defined.
        // Show link to activity.
        array_splice( $PAGE->menu, 0, 0, array( array( 'name' => 'folio_feeds',
            'html' => '<li><a href="'.$CFG->wwwroot . 'activity/fac/' . $currentusername . '/summary/all/all/1">Your Activity</a></li>')));
    }

    // Setup sidebar
    folio_sidebar_init();
    // Update top menu
    folio_prunetopmenu();
}

/**
* Remove any non alphanumeric characters in a string
* Legal Characters are a-z, A-Z, 0-9, -, _, and () and spaces
*
* @param string $input
* @param array $valid Valid characterrs, defaults to standard -_() and spaces.
* @return string Clean input
**/
function folio_clean( $input, $valid = array('(',')',' ','-','_',' ' )) {

    $input = trim( $input );

    // Test for an easy clean / validation.
    $i = $input;
    foreach ( $valid as $v) {
        $i = str_replace( $v, '', $i);
    }

    if ( ctype_alnum( $i ) ) {
        // Validated.  Return
        return $input;
    } else {
        // Step thru & clean each character.

        $retval = '';

        for ( $i = 0; $i < strlen($input); $i++ ) {
            $v = substr( $input, $i, 1 );
            if ( ctype_alnum( $v ) | in_array( $v, $valid ) ) {
                $retval .= $v;
            }
        }

        return $retval;
    }
}

/**
* Publish something to an RSS feed
*
*
* @param int owner_ident (int for the object owner)
* @param string owner_username (url-able string name for the object owner)
* @param int user_ident (int for the person doing the action)
* @param string user_username (url-able string name for the user who performed the action)
* @param string user_name (the pretty name of the user who performed the action)
* @param string Type (url-able string, etc: weblog, file, wikipage)
* @param int Type_Ident (string: can be used for update/delete.  Should be unique
    with respect to the type, aka File2, wiki2939, blog234.  I like this
    way of keeping track of unique items (as opposed to the link or some
    other attribute) because all of the add-ins and native data types have
    an ident value in the database to store their values.  It would
    probably make sense to allow this field to be null, in which case, the
    rss item could not be updated/deleted in the future.).
* @param array Tags (an array of string, eg. array("wiki", "elgg", "notes") ).
* @param string Title (string title for the RSS item).
* @param string Body (full string for the message to be syndicated.)
* @param string Link (the http address to view/comment on/edit/whatever the item).
* @param string access (the permission level for the rss item, def=public).
**/
function rss_additem( $owner_ident, $owner_username, $user_ident, $user_name, $user_username, $type, $type_ident, $tags, $title, $body, $link, $access='PUBLIC', $created = -1 ) {

    if ($created == -1 ) {
        $created = time();
    }

    $rss = new StdClass;
    $rss->owner_ident = intval($owner_ident);
    $rss->owner_username = $owner_username;
    $rss->user_ident = intval($user_ident);
    $rss->user_name = $user_name;
    $rss->user_username = $user_username;
    $rss->type = $type;
    $rss->type_ident = $type_ident;
    $rss->title = $title;
    $rss->body = $body;
    $rss->link = $link;
    $rss->created = $created;
    $rss->access = $access;

    // Insert new record into db.
    insert_record('folio_rss',$rss);
}


/**
* Deletes all RSS feed entries with the given key and type.
*    Called to prevent duplicate entries for rapidly edited documents.
*    If calling with the user_ident and timespan, need to use both.
*
* @param string Type (url-able string, etc: weblog, file, wikipage)
* @param int Type_Ident
* @param int User_Ident  (can be used to limit the deletion of the item to only the current user)
* @param int timespan (how far back to delete)
**/
function rss_deleteitem( $type, $type_ident, $user_ident = -1, $timespan = -1 ) {
    global $db, $CFG;

    if ( $user_ident == -1 AND $timespan == -1 ) {
        delete_records('folio_rss', 'type_ident', $type_ident, 'type', $type);
    } else {
        $stmt = $db->Prepare("DELETE FROM {$CFG->prefix}folio_rss where type = '{$type}' AND " .
                             "type_ident = {$type_ident} AND user_ident = {$user_ident} AND created > " .
                             (time() - $timespan ));
        $returnvalue = $db->Execute($stmt);
    }
}

function folio_rss_page_updated($object_type, $event, $object ) {
	global $FOLIO_CFG, $db, $CFG;
    $page = $object;
	$user = folio_user::SelectWhereIdent($page->user_ident);
	
    // If configured, remove all old history entries from the RSS feed.
    if ( $FOLIO_CFG->page_deleteoldpagesfromrss ) {
        // Delete
        rss_deleteitem( 'page', $page->page_ident );
    }

    // Delete all RSS items with the same user within the last hour.
    //	This is important for ajax, as it does a lot of saves.
    rss_deleteitem( 'page', $page->page_ident, $_SESSION['userid'], 3600);
   
    // Grab the security level.
    $security = folio_page_permission::SelectHighestWherePageIdent( $page->page_ident );

    
    $body = new folio_PageBodyView( $page );
    $body->Load( new folio_PageAttachmentView( $page ));
    $body = $body->html();
    
    // Create RSS record
    rss_additem( $page->user_ident, $user->username,
        $page->creator_ident, $_SESSION['name'], $_SESSION['username'],
        'page', $page->page_ident,
        '', $page->title,
        $body,
        url . "page/{$page->page_ident}",
        $security
        );


    // If we're changing to private permission level, then go ahead and change
    //        past entries to private as well.
    if ( strtolower(substr( $security,0,4)) == 'user'
            OR strtolower(substr( $security,0,9)) == 'community'
            OR strtolower($security) == 'private' ) {

        $stmt = $db->Prepare( "UPDATE {$CFG->prefix}folio_rss SET access = '{$security}'
                WHERE type_ident = {$page->page_ident} AND type='page' AND access <> 'PRIVATE'");
        $returnvalue = $db->Execute($stmt);
    }

    // Update any comments to match the same permission level.
    $stmt = $db->Prepare("UPDATE {$CFG->prefix}folio_comment SET access = '{$security}' " .
                         "WHERE item_ident = {$page->page_ident} AND item_type = 'page'" );
    $returnvalue = $db->Execute($stmt);

    // Update RSS of any comments to match the same permission level.
    $stmt = $db->Prepare("UPDATE {$CFG->prefix}folio_rss SET access = '{$security}' " .
                         "WHERE type_ident IN ( SELECT activity_ident FROM {$CFG->prefix}folio_comment WHERE item_ident =  " .
                         " {$page->page_ident} AND item_type = 'page') AND type = 'page_comment'" );
    $returnvalue = $db->Execute($stmt);
	
    return $page;
}


function folio_rss_page_comment_inserted($object_type, $event, $object ) {
	global $FOLIO_CFG, $db, $CFG;
    $comment = $object;
    $page = folio_page::SelectWherePageIdent( $comment->object_id );
 	$user = folio_user::SelectWhereIdent($page->user_ident);
 	
    // Grab the security level.
    $security = folio_page_permission::SelectHighestWherePageIdent( $page->page_ident );
    
    // Create RSS record
    rss_additem( $page->user_ident, $user->username,
        $comment->owner, $_SESSION['name'], $_SESSION['username'],
        'page_comment', 'Comment on "' . $page->title . '" by "' . $_SESSION['name'],
        '', 'Comment on "' . $page->title . '" by "' . $_SESSION['name'],
        $comment->body,
        url . "page/{$page->page_ident}#{$comment->ident}",
        $security
        );
        
    return $comment;
}

/**
* Retrieve records from an rss feed.
*
* @param string $where (There where clause for the query)
* @param int $from (Where to start showing results from, def = 0
* @param int $perpage How many results to show after the start, def = 10
* @param array OUTPUT (The returned records in an array of arrays corresponding to the mysql records.)
**/
function rss_getitems( $where, $from=0, $perpage=10 ) {
    global $CFG;

   $results = recordset_to_array(get_recordset_sql( "SELECT CONCAT(type_ident, '.', type, '.', created) masterkey, " .
        $CFG->prefix . "folio_rss.* FROM " . $CFG->prefix . "folio_rss" .
        " WHERE $where" .
        " ORDER BY created DESC " .
        " LIMIT $from, $perpage " ));
    return $results;
}

/**
* Retrieve the number of records available in an RSS feed.
*
* @param string $where ( the where clause for the query)
* @param array OUTPUT an Integer value.
**/
function rss_getcount( $where ) {
    global $CFG;
    // Clean incoming variables.

   $results = recordset_to_array(get_recordset_sql( "SELECT '1' as ident, count(*) as records " .
        " FROM " . $CFG->prefix . "folio_rss" .
        " WHERE $where " ));

    return intval($results[1]->records);

}

/**
* Retrieve all of the allowable permission types for the passed userid
* @param int $userid -1 is a valid option, just sets to public permissions.
**/
function rss_permissionlist( $userid ) {
    global $CFG;

    if ( $userid == -1 ) {
        return array('PUBLIC', 'MODERATED');
    }

    // Default permissions already availabled to logged-in users.
    $list = array('PUBLIC', 'LOGGED_IN', 'MODERATED');

    // Find values for joined communities.
    $communities = recordset_to_array(get_recordset_sql(
        "SELECT distinct u.ident as i, u.ident FROM " . $CFG->prefix . "friends e inner join " .
            $CFG->prefix . "users u on e.friend = u.ident where e.owner=$userid and user_type = 'community'"));

    if ( $communities) {
        foreach ( $communities as $community) {
            $list[] = 'community' . $community->ident;
        }
    }

    // Find values for owned communities
    $communities = recordset_to_array(get_recordset_sql(
        "SELECT DISTINCT ident as username, ident FROM " . $CFG->prefix . "users where owner = $userid"));

    if ( $communities ) {
        foreach ( $communities as $community ) {
            $list[] = 'community' . $community->username;
        }
    }

    // Find values for joined groups.
    $groups = recordset_to_array(get_recordset_sql(
        "SELECT distinct g.ident as i, g.ident FROM " . $CFG->prefix . "group_membership m inner join " .
            $CFG->prefix . "groups g on m.group_id = g.ident where m.user_id=$userid "));

    if ( $groups) {
        foreach ( $groups as $group) {
            $list[] = 'group' . $group->ident;
        }
    }

    // Find values for owned groups
    $groups = recordset_to_array(get_recordset_sql(
        "SELECT DISTINCT ident as i, ident FROM " . $CFG->prefix . "groups where owner = $userid"));

    if ( $groups ) {
        foreach ( $groups as $group ) {
            $list[] = 'group' . $group->ident;
        }
    }

    // Add user{$userid} for stuff set to private for the specified user.
    $list[] = 'user' . $userid;


    return $list;
}

/**
* Post the new weblog post to the RSS table.  Also called for weblog edits.
**/
function folio_rss_weblog_post($object_type, $event, $object) {
    global $db, $CFG;

    $title = $object->title;
    $body = $object->body;
        $access = $object->access;

    // Owner is where the blog is located, aka, the community.
    $owner_id = $object->weblog;
    $owner_username = user_info("username", $owner_id);

    // Get user info from db.
    $user_id = $object->owner;
    $user_name = user_info("name", $user_id);
    $user_username = user_info("username", $user_id);

    $type =  'weblog';

    // Ugly hack to grab tags.  Only works for new posts, not for edits.  Need to fix.
    // TODO: Fix to work with edits.
    $tags = explode(',', trim(optional_param('new_weblog_keywords')));

    $type_ident = $object->ident;

    $link = $CFG->wwwroot . $owner_username . '/weblog/' . $type_ident . '.html';
    $created = $object->posted;

    //generate rss
    rss_deleteitem( $type, $type_ident );
    rss_additem( $owner_id, $owner_username, $user_id, $user_name, $user_username, $type, $type_ident, $tags, $title, $body, $link, $access, $created);

    // Update RSS in comments to match the same permission level.
    $stmt = $db->Prepare("UPDATE {$CFG->prefix}folio_rss SET access = '{$access}' " .
                         "WHERE type_ident IN ( SELECT ident FROM {$CFG->prefix}weblog_comments " .
                         "WHERE post_id = {$type_ident} ) AND type = 'weblog_comment'" );
    $returnvalue = $db->Execute($stmt);

    // return results
    return $object;
}

/**
* Deletes the post from the RSS table.
**/
function folio_rss_weblog_delete($object_type, $event, $object) {
    global $db, $CFG;

    $stmt = $db->Prepare( "DELETE FROM {$CFG->prefix}folio_rss
            WHERE type_ident = {$object->ident} AND type='weblog'");
    $returnvalue = $db->Execute($stmt);

    // return results
    return $object;
}

/**
* Deletes the post from the RSS table.
**/
function folio_rss_weblog_comment_delete($object_type, $event, $object) {
    global $db, $CFG;
    $stmt = $db->Prepare( "DELETE FROM {$CFG->prefix}folio_rss
            WHERE type_ident = {$object->ident} AND type='weblog_comment'");
    $returnvalue = $db->Execute($stmt);

    // return results
    return $object;
}

/**
* Post the new weblog comment to the RSS table
**/
function folio_rss_weblog_comment_post($object_type, $event, $object) {
    global $CFG;

    $post = get_record_select('weblog_posts','ident = ' . $object->post_id);

    if (!$post) {
        trigger_error("Invalid weblog ident {$comment->post_id} passed to /mod/folio/lib.php ");
    }

    // Owner is where the blog is located, aka, the community.
    $owner_id = $post->weblog;
    $owner_username = user_info("username", $owner_id);
    $access = $post->access;

    // Get user info from db.
    $user_id = $object->owner;

    if ( $user_id == 0 ) {
        // Guest
        $user_id = -1;
        $user_name = $object->postedname;
        $user_username = '';
    } else {
        // Logged in users
        $user_name = $object->postedname;
        $user_username = user_info("username", $user_id);
    }

    $title = 'Comment on "' . $post->title . '"';
    $link = url . $owner_username. "/weblog/" . $post->ident . ".html#" . $object->ident;
    $created = $object->posted;

    //generate rss
    rss_additem( $owner_id, $owner_username,
        $user_id, $user_name, $user_username,
        'weblog_comment', $object->ident, '',
        $title, $object->body, $link, $access, $created );
}

/**
* Record a file upload or edit
**/
function folio_rss_file_post($object_type, $event, $object) {
    global $CFG;

    // Set type
    $type = 'file';

    // Grab tags from the post variables.
    $tags = explode(',', trim(optional_param('new_file_keywords')));

    // Pull the poster information from session
    if ( isloggedin() ) {
        // Get current logged in user
        $user_ident = $_SESSION['userid'];
        $user_name = $_SESSION['name'];
        $user_username = $_SESSION['username'];
    } else {
        // Build variables for a guest uploader.  Not sure if possible,
        //    but just in case...
        $user_ident = -1;
        $user_name = 'Guest';
        $user_username = '';
    }

    // Build variables from $object
    $type_ident = $object->ident;
    $owner_ident = $object->files_owner;

    if ( $event == 'update' ) {
        // Owner isn't passed, look it up
        $file = get_record_select('files','ident = ' . $type_ident );
        if (!$file) {
            trigger_error("Invalid file ident {$type_ident} passed to /mod/folio/lib.php ");
        }
        // Owner is where the blog is located, aka, the community.
        $owner_ident = $file->files_owner;
    }
    $owner_username = run('users:id_to_name', $owner_ident);

    if ( strlen( $object->title ) > 0 ) {
        $title = "File $event: {$object->title}";
    } else {
        $title = "File $event: {$object->originalname}";
    }
    $body = $object->description;
    $link = url . $owner_username . '/files/' . $object->folder . '/' . $type_ident . '/filename' ;
    $access = $object->access;
    $created = $object->time_uploaded;

    //generate rss
    rss_deleteitem( $type, $type_ident );
    rss_additem( $owner_ident, $owner_username, $user_ident, $user_name, $user_username, $type, $type_ident, $tags, $title, $body, $link, $access, $created );

    return $object;
}

/**
* Deletes the file from the RSS table.
**/
function folio_rss_file_delete($object_type, $event, $object) {
    global $db, $CFG;
    $stmt = $db->Prepare( "DELETE FROM {$CFG->prefix}folio_rss
            WHERE type_ident = {$object->ident} AND type='file'");
    $returnvalue = $db->Execute($stmt);

    // return results
    return $object;
}

/**
* Return the ident for the user reading this page
*   If no validation, then return -1
**/
function folio_decodehash( $readerhash ) {

    // To view subscribe records, validate hash.  If fail, return false.
    if ( $readerhash == '' ) {
        return -1;
    } else {
        // Calculate MD5 hash
        $i = strpos( $readerhash, ':' );

        // Separate variables
        $reader_name = substr( $readerhash, 0, $i );
        $reader_pwd = substr( $readerhash, $i+1, strlen($readerhash) - $i -1);
        // Grab ID
        $rec = get_record('users','username',$reader_name);

        if ( !$rec ) {
            // No matching user
            return -1;
        } elseif ( $rec->password == $reader_pwd ) {
            // Matched pwd
            return $rec->ident;
        } else {
            // Pwd doesn't match
            return -1;
        }

    }
}

/**
* Return the hash values for a userident
**/
function folio_createhash( $user_ident ) {
    if ( isloggedin() ) {
        $rec = get_record('users','ident',$user_ident);
        return $rec->username . ':' . $rec->password;
    }
}

/**
* Update the page setup
*
* Run this in the top menus.  Can't run this in the lib.php init setup, as
*    not all of the add-ins have been loaded.
**/
function folio_prunetopmenu() {
    global $PAGE;
    global $FOLIO_CFG;

    $menu = $PAGE->menu;
    $PAGE->menu = array();

    // Top Menu
    foreach ( $menu as $m ) {
        if ( !in_array( $m['name'], $FOLIO_CFG->disabled_topmenuitems ) ) {
            $PAGE->menu[] = $m;
        }
    }
}

?>
